import { Subscription } from "egg";
import * as fs from "fs";
import * as path from "path";

/**
 * 日志清理定时任务
 * 开发环境：每10分钟清理所有日志文件
 * 生产环境：每5分钟清理过期和过大的日志文件
 */
export default class LogCleanupSchedule extends Subscription {
  /**
   * 订阅的条件
   */
  static get schedule() {
    return {
      interval: "10m", // 每10分钟执行一次
      type: "all", // 指定所有的 worker 都需要执行
      immediate: false // 启动后不立即执行
    };
  }

  /**
   * 执行任务的具体内容
   */
  async subscribe() {
    const { ctx, app } = this;
    const logger = ctx.logger;

    try {
      logger.info("[定时任务] 开始执行日志清理任务");

      const logsDir = path.join(app.config.baseDir, "logs");

      if (!fs.existsSync(logsDir)) {
        logger.info("[定时任务] 日志目录不存在，跳过清理");
        return;
      }

      // 检查是否为开发环境
      const isDev = app.config.env === "local" || process.env.NODE_ENV === "development";

      let stats;
      if (isDev) {
        // 开发环境：清理所有日志
        logger.info("[定时任务] 开发环境 - 清理所有日志文件");
        stats = await this.clearAllLogs(logsDir);
      } else {
        // 生产环境：智能清理
        logger.info("[定时任务] 生产环境 - 智能清理日志文件");
        stats = await this.cleanupLogs(logsDir);
      }

      logger.info(
        `[定时任务] 日志清理完成: 清理了 ${stats.deletedFiles} 个文件，释放空间 ${this.formatBytes(stats.freedSpace)}`
      );
    } catch (error) {
      logger.error("[定时任务] 日志清理失败:", error);
    }
  }

  /**
   * 清理所有日志文件（开发环境专用）
   * @param logsDir 日志目录路径
   */
  private async clearAllLogs(
    logsDir: string
  ): Promise<{ deletedFiles: number; freedSpace: number; deletedDirs: number }> {
    let deletedFiles = 0;
    let deletedDirs = 0;
    let freedSpace = 0;

    const processDirectory = async (currentPath: string) => {
      if (!fs.existsSync(currentPath)) {
        return;
      }

      const files = fs.readdirSync(currentPath);

      for (const file of files) {
        const filePath = path.join(currentPath, file);
        const stat = fs.statSync(filePath);

        if (stat.isDirectory()) {
          // 递归处理子目录
          await processDirectory(filePath);
          // 删除空目录
          try {
            fs.rmdirSync(filePath);
            deletedDirs++;
            this.ctx.logger.info(`[定时任务] 删除目录: ${path.relative(logsDir, filePath)}`);
          } catch (err) {
            // 目录可能不为空，忽略错误
          }
        } else if (stat.isFile()) {
          // 删除所有文件
          try {
            const fileSize = stat.size;
            fs.unlinkSync(filePath);
            deletedFiles++;
            freedSpace += fileSize;
            this.ctx.logger.info(
              `[定时任务] 删除文件: ${path.relative(logsDir, filePath)} (${this.formatBytes(fileSize)})`
            );
          } catch (err) {
            this.ctx.logger.warn(`[定时任务] 删除文件失败: ${file}`, err);
          }
        }
      }
    };

    await processDirectory(logsDir);

    // 清理根目录下的文件
    try {
      const rootFiles = fs.readdirSync(logsDir);
      for (const file of rootFiles) {
        const filePath = path.join(logsDir, file);
        const stat = fs.statSync(filePath);
        if (stat.isFile()) {
          const fileSize = stat.size;
          fs.unlinkSync(filePath);
          deletedFiles++;
          freedSpace += fileSize;
          this.ctx.logger.info(`[定时任务] 删除根文件: ${file} (${this.formatBytes(fileSize)})`);
        }
      }
    } catch (err) {
      // 忽略错误
    }

    return { deletedFiles, freedSpace, deletedDirs };
  }

  /**
   * 清理日志文件（生产环境智能清理）
   * @param logsDir 日志目录路径
   */
  private async cleanupLogs(logsDir: string): Promise<{ deletedFiles: number; freedSpace: number }> {
    let deletedFiles = 0;
    let freedSpace = 0;

    const maxFileAge = 7 * 24 * 60 * 60 * 1000; // 7天
    const maxFileSize = 100 * 1024 * 1024; // 100MB
    const now = Date.now();

    const processDirectory = async (dirPath: string) => {
      const files = fs.readdirSync(dirPath);

      for (const file of files) {
        const filePath = path.join(dirPath, file);
        const stat = fs.statSync(filePath);

        if (stat.isDirectory()) {
          await processDirectory(filePath);
        } else if (stat.isFile() && file.endsWith(".log")) {
          const fileAge = now - stat.mtime.getTime();
          const fileSize = stat.size;

          // 清理条件：
          // 1. 文件年龄超过7天
          // 2. 文件大小超过100MB
          // 3. 是调试日志文件（保留错误日志）
          const shouldDelete =
            fileAge > maxFileAge ||
            fileSize > maxFileSize ||
            ((file.includes("debug") || file.includes("info")) && !file.includes("error") && !file.includes("warn"));

          if (shouldDelete) {
            try {
              fs.unlinkSync(filePath);
              deletedFiles++;
              freedSpace += fileSize;
              this.ctx.logger.info(`[定时任务] 删除日志文件: ${file} (${this.formatBytes(fileSize)})`);
            } catch (err) {
              this.ctx.logger.warn(`[定时任务] 删除文件失败: ${file}`, err);
            }
          }
        }
      }
    };

    await processDirectory(logsDir);

    return { deletedFiles, freedSpace };
  }

  /**
   * 格式化字节数
   */
  private formatBytes(bytes: number): string {
    if (bytes === 0) return "0 Bytes";

    const k = 1024;
    const sizes = ["Bytes", "KB", "MB", "GB"];
    const i = Math.floor(Math.log(bytes) / Math.log(k));

    return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i];
  }
}
